#include "stdafx.h"
#include "CircleDequeueBase.h"
#include <memory>
#include <iostream>
#include "MemoryPool.h"
#include "DataDefinition.h"

CircleDequeueBase::CircleDequeueBase(unsigned int p_uiEntireBufferSize,unsigned int p_uiElementAmount, unsigned int p_uiElementSize,unsigned int p_uiBeginSequence,unsigned int p_uiEndSequence,unsigned char p_ucEntireDataType, unsigned char p_ucIncrementDataType)
	:m_uiElementAmount(p_uiElementAmount)
	,m_uiElementSize(p_uiElementSize)
	,m_stDequeueHeader()
	,m_pstCurrentAddr(NULL)
	,m_bIsReset(false)
	,m_csLock()
	,m_pszSendBuffer(NULL)
	,m_uiSendBufferLength(0)
	,m_uiSendBufferIndex(0)
	,m_pszEntireBuffer(NULL)
	,m_uiEntireBufferSize(p_uiEntireBufferSize)
	,m_uiEntireBufferLength(0)
	,m_pszCache(NULL)
	,m_uiCacheLength(0)
	,m_szCompressCache(NULL)
	,m_uiComCacheLength(0)
	,m_vecRookieSocketList()
	,m_vecAlivedSocketList()
	,m_uiBeginSequence(p_uiBeginSequence)
	,m_uiEndSequence(p_uiEndSequence)
	,m_uiCurrentSequence(p_uiBeginSequence)
	,m_iLastError(CD_DEFAULT)
	,m_vecObserverList()
	,m_ucEntireDataType(p_ucEntireDataType)
	,m_ucIncrementDataType(p_ucIncrementDataType)
	,m_uiCount(0)
	,m_clsLastDate()
{
	m_clsLastDate.SetToCurrentDate();
}

CircleDequeueBase::~CircleDequeueBase()
{
	DequeueElement* pElement = m_stDequeueHeader.m_pHeader;
	DequeueElement* pNext = NULL;
	do
	{
		pNext = pElement->m_pNext;
		delete pElement;
		pElement = pNext;
	}while(pElement != m_stDequeueHeader.m_pHeader);

	if(NULL != m_pszSendBuffer)
	{
		free(m_pszSendBuffer);
	}

	if(NULL != m_pszEntireBuffer)
	{
		free(m_pszEntireBuffer);
	}

	if(NULL != m_pszCache)
	{
		free(m_pszCache);
	}

	if(NULL != m_szCompressCache)
	{
		free(m_szCompressCache);
	}
}

bool CircleDequeueBase::Initialize()
{
	// Allocate first element
	DequeueElement* pFirst = new DequeueElement();
	if(NULL == pFirst)
	{
		LOG(FATAL) << "Cannot new instance of DequeueElement" << endl;
		return false;
	}

	pFirst->m_iIndex = 0;
	pFirst->m_uiSize = m_uiElementSize;
	pFirst->m_szElementAddr = (char*)malloc(m_uiElementSize);
	if(NULL == pFirst->m_szElementAddr)
	{
		LOG(FATAL) << "Cannot malloc buffer of element" << endl;
		return false;
	}

	DequeueElement* pPreElement = pFirst;
	m_stDequeueHeader.m_pHeader = pFirst;
	m_pstCurrentAddr = pFirst;

	// Allocate other elements
	for(int i = 1; i < m_uiElementAmount; i++)
	{
		DequeueElement* pElement = new DequeueElement();
		pElement->m_iIndex = i;
		pElement->m_uiSize = m_uiElementSize;
		pElement->m_szElementAddr = (char*)malloc(m_uiElementSize);

		pPreElement->m_pNext = pElement;
		pElement->m_pPrevious = pPreElement;
		pPreElement = pElement;
	}

	// associate element with the first one
	pFirst->m_pPrevious = pPreElement;
	pPreElement->m_pNext = pFirst;

	//Initialize SRWLock
	InitializeCriticalSection(&m_csLock);

	// Allocate entire buffer
	m_uiSendBufferSize = m_uiEntireBufferSize;
	m_pszSendBuffer = (char*)malloc(m_uiSendBufferSize);
	m_pszEntireBuffer = (char*)malloc(m_uiEntireBufferSize);
	m_uiCacheSize = m_uiEntireBufferSize;
	m_pszCache = (char*)malloc(m_uiCacheSize);
	m_uiComCacheSize = m_uiEntireBufferSize * 2;
	m_szCompressCache = (char*)malloc(m_uiComCacheSize);
	memset(m_pszSendBuffer,0,m_uiSendBufferSize);
	memset(m_pszEntireBuffer,0,m_uiEntireBufferSize);
	memset(m_pszCache,0,m_uiCacheSize);
	memset(m_szCompressCache,0,m_uiComCacheSize);

	return true;
}

void CircleDequeueBase::PrintDequeue(int p_iSequence)
{
	// Require lock
	EnterCriticalSection(&m_csLock);

	DequeueElement* pElement = m_stDequeueHeader.m_pHeader;
	char szLog[200];
	memset(szLog,0,200);

	if(0 == p_iSequence)
	{// Print dequeue elements in positive sequence
		do
		{
			sprintf(szLog,"m_uiSize : %d, m_uiAllocedSize : %d, m_iIndex : %d, m_iBaseIndex : %d\r\n",pElement->m_uiSize, pElement->m_uiAllocedSize, pElement->m_iIndex, pElement->m_iEntireIndex);
			cout << szLog << endl;
			memset(szLog,0,200);
			pElement = pElement->m_pNext;
		}while(pElement != m_stDequeueHeader.m_pHeader);
	}
	else
	{// Print dequeue elements in negative sequence
		do
		{
			sprintf(szLog,"m_uiSize : %d, m_uiAllocedSize : %d, m_iIndex : %d, m_iBaseIndex : %d\r\n",pElement->m_uiSize, pElement->m_uiAllocedSize, pElement->m_iIndex, pElement->m_iEntireIndex);
			cout << szLog << endl;
			memset(szLog,0,200);
			pElement = pElement->m_pPrevious;
		}while(pElement != m_stDequeueHeader.m_pHeader);
	}

	// Release shared lock
	LeaveCriticalSection(&m_csLock);
}

bool CircleDequeueBase::ResetDequeue()
{
	DequeueElement* pElement = m_stDequeueHeader.m_pHeader;
	if(NULL == pElement)
	{
		// TO-DO : Set error code
		return false;
	}

	do
	{
		if(pElement->m_uiSize > m_uiElementSize)
		{
			// Delete element
			pElement = DeleteElement(pElement);
		}
		else
		{
			pElement->m_iEntireIndex = -1;
			pElement->m_iIndex = -1;
			memset(pElement->m_szElementAddr,0,pElement->m_uiSize);
			pElement->m_uiAllocedSize = 0;
			pElement = pElement->m_pNext;
		}
	}while(pElement != m_stDequeueHeader.m_pHeader);

	return true;
}

DequeueElement* CircleDequeueBase::AddElementBefore(DequeueElement* p_pstElement,unsigned int p_uiSize)
{
	if(NULL == p_pstElement)
	{
		return NULL;
	}

	DequeueElement* pstNew = new DequeueElement();
	pstNew->m_szElementAddr = (char*)malloc(p_uiSize);
	pstNew->m_uiSize = p_uiSize;
	pstNew->m_iIndex = m_uiElementAmount;

	// Connect elements before
	DequeueElement* pstPre = p_pstElement->m_pPrevious;
	pstNew->m_pPrevious = pstPre;
	pstNew->m_pNext = p_pstElement;
	p_pstElement->m_pPrevious = pstNew;
	pstPre->m_pNext = pstNew;

	return pstNew;
}

DequeueElement* CircleDequeueBase::AddElementBehind(DequeueElement* p_pstElement,unsigned int p_uiSize)
{
	if(NULL == p_pstElement)
	{
		return NULL;
	}

	DequeueElement* pstNew = new DequeueElement();
	pstNew->m_szElementAddr = (char*)malloc(p_uiSize);
	pstNew->m_uiSize = p_uiSize;
	pstNew->m_iIndex = m_uiElementAmount;

	// Connect elements behind
	DequeueElement* pstNext = p_pstElement->m_pNext;
	pstNew->m_pPrevious = p_pstElement;
	pstNew->m_pNext = p_pstElement->m_pNext;
	p_pstElement->m_pNext = pstNew;
	pstNext->m_pPrevious = pstNew;

	return pstNew;
}

DequeueElement* CircleDequeueBase::DeleteElement(DequeueElement* p_pstElement)
{
	if(NULL == p_pstElement)
	{
		return NULL;
	}

	DequeueElement* pstPre = p_pstElement->m_pPrevious;
	DequeueElement* pstNext = p_pstElement->m_pNext;

	pstPre->m_pNext = pstNext;
	pstNext->m_pPrevious = pstPre;

	delete p_pstElement;
	p_pstElement = NULL;

	return pstNext;
}

int CircleDequeueBase::GetLastError()
{
	return m_iLastError;
}

void CircleDequeueBase::Attach(ObserverBase* p_pcObserverBase)
{
	// Require lock
	EnterCriticalSection(&m_csLock);

	if(NULL == p_pcObserverBase)
	{
		LOG(WARNING) << "Observer instance need to be added is NULL";
		LeaveCriticalSection(&m_csLock);
		return;
	}

	m_vecObserverList.push_back(p_pcObserverBase);

	// Release Lock
	LeaveCriticalSection(&m_csLock);
}

void CircleDequeueBase::Detach(ObserverBase* p_pcObserverBase)
{
	// Require lock
	EnterCriticalSection(&m_csLock);

	if(NULL == p_pcObserverBase)
	{
		LOG(WARNING) << "Observer instance need to be deleted is NULL";
		LeaveCriticalSection(&m_csLock);
		return;
	}

	vector<ObserverBase*>::iterator it = m_vecObserverList.begin();
	while(it != m_vecObserverList.end())
	{
		if(*it == p_pcObserverBase)
		{
			break;
		}
		it++;
	}

	m_vecObserverList.erase(it);

	// Release Lock
	LeaveCriticalSection(&m_csLock);
}

//void CircleDequeue::AddSocket(SOCKET p_iClient)
//{
//	// Require lock
//	EnterCriticalSection(&m_csLock);
//
//	m_vecRookieSocketList.push_back(p_iClient);
//
//	// Release Lock
//	LeaveCriticalSection(&m_csLock);
//}
//
void CircleDequeueBase::DeleteSocket(SOCKET p_stSocket)
{
	// Require lock
	EnterCriticalSection(&m_csLock);

	vector<SOCKET>::iterator it = m_vecAlivedSocketList.begin();
	while(it != m_vecAlivedSocketList.end())
	{
		if(*it == p_stSocket)
		{
			LOG(INFO) << "Socket : " << p_stSocket << " has been deleted from alive list. File type : " << (unsigned int)m_ucEntireDataType;
			m_vecAlivedSocketList.erase(it);
			break;
		}
		it++;
	}

	it = m_vecRookieSocketList.begin();
	while(it != m_vecRookieSocketList.end())
	{
		if(*it == p_stSocket)
		{
			LOG(INFO) << "Socket : " << p_stSocket << " has been deleted from rookie list. File type :" << (unsigned int)m_ucEntireDataType;
			m_vecRookieSocketList.erase(it);
			break;
		}
		it++;
	}

	// Release Lock
	LeaveCriticalSection(&m_csLock);
}

void CircleDequeueBase::ClearSocket(vector<SOCKET> p_vecExceptSockets)
{
	EnterCriticalSection(&m_csLock);
	if (p_vecExceptSockets.size() == 0) // Clear list if no exceptions
	{
		m_vecRookieSocketList.clear();
		m_vecAlivedSocketList.clear();
		LOG(INFO) << "All sockets int circle queue have been deleted. File type : " << (unsigned int)m_ucEntireDataType;
	}
	else // Clean up list with exceptions
	{
		RemoveClientWithException(m_vecRookieSocketList,p_vecExceptSockets);
		RemoveClientWithException(m_vecAlivedSocketList,p_vecExceptSockets);
		LOG(INFO) << "Sockets with exceptions have been deleted. File type : " << (unsigned int)m_ucEntireDataType;
	}

	LeaveCriticalSection(&m_csLock);
}

void CircleDequeueBase::RemoveClientWithException(vector<SOCKET>& p_vecSourceList, vector<SOCKET>& p_vecExceptionList)
{
	// Initialize tempt list from source list
	vector<SOCKET> vecTempt = p_vecSourceList;
	vector<SOCKET>::iterator it = vecTempt.begin();
	while(it != vecTempt.end())
	{
		// Find if socket in source list and exception list is match
		bool bIsFound = false;
		vector<SOCKET>::iterator itExcept = p_vecExceptionList.begin();
		while (itExcept != p_vecExceptionList.end())
		{
			if (*itExcept == *it)
			{
				bIsFound = true;
				break;
			}

			itExcept++;
		}

		// If found matched socket, erase socket from source list
		if (bIsFound == true)
		{
			vector<SOCKET>::iterator itSourceList = p_vecSourceList.begin();
			while(itSourceList != p_vecSourceList.end())
			{
				if (*it == *itSourceList)
				{
					p_vecSourceList.erase(itSourceList);
					break;
				}

				itSourceList++;
			}
		}

		it++;
	}
}

bool CircleDequeueBase::Notify(vector<SOCKET>& p_vecSocketList, char* p_szBuffer, unsigned int p_uiBufferSize)
{
	// Calculate observer and socket size
	unsigned int uiObserverAmount = m_vecObserverList.size();
	unsigned int uiClientSocketAmount = p_vecSocketList.size();
	if(0 == uiObserverAmount || 0 == uiClientSocketAmount)
	{
		return true;
	}

	MemoryPool* pcInstance = MemoryPool::Instance();
	if(NULL == pcInstance)
	{
		LOG(ERROR) << "Null instance of memory pool";
		return false;
	}

	// Check if index is available in memory pool
	while(false == pcInstance->IsIDAvailable(m_uiCurrentSequence))
	{
		m_uiCurrentSequence++;
	}

	unsigned int iCount = 0; // For test
	vector<SOCKET>::iterator it = p_vecSocketList.begin();
	vector<SOCKET> vecDeleteList;
	unsigned int uiIndex = 0;
	while(it != p_vecSocketList.end())
	{
		unsigned int uiObserverIndex = uiIndex % uiObserverAmount;
		if(false == m_vecObserverList[uiObserverIndex]->OnNotify(*it,p_szBuffer,p_uiBufferSize, m_uiCurrentSequence))
		{
			iCount--; // For test
			vecDeleteList.push_back(*it);
		}

		uiObserverIndex++;
		it++;
		uiIndex++;
		iCount++;// For test
	}

	RemoveClient(p_vecSocketList,vecDeleteList);
	cout << "Sent to " << iCount << " client(s)" << endl;// For test
	return true;
}

void CircleDequeueBase::RemoveClient(vector<SOCKET>& p_vecSourceList, vector<SOCKET>& p_vecDeleteList)
{
	if(0 == p_vecDeleteList.size())
	{
		return;
	}

	vector<SOCKET>::iterator it = p_vecDeleteList.begin();
	while(it != p_vecDeleteList.end())
	{
		vector<SOCKET>::iterator itSource = p_vecSourceList.begin();
		while(itSource != p_vecSourceList.end())
		{
			if(*it == *itSource)
			{
				LOG(INFO) << "Delete socket " << *itSource << " from circle queue socket list";
				p_vecSourceList.erase(itSource);
				break;
			}
			itSource++;
		}
		it++;
	}
}

bool CircleDequeueBase::SendPreviousPackets(vector<SOCKET> p_vecSocketList,DequeueElement* p_stElement)
{
	// Calculate observer and socket size
	unsigned int uiObserverAmount = m_vecObserverList.size();
	unsigned int uiClientSocketAmount = p_vecSocketList.size();

	if(0 == uiObserverAmount || 0 == uiClientSocketAmount)
	{
		LOG(WARNING) << "No observer or socket in the queue";
		return false;
	}

	MemoryPool* pcInstance = MemoryPool::Instance();
	if(NULL == pcInstance)
	{
		LOG(ERROR) << "Null instance of memory pool";
		return false;
	}

	// Check if index is available in memory pool
	while(false == pcInstance->IsIDAvailable(m_uiCurrentSequence))
	{
		m_uiCurrentSequence++;
	}

	// Send previous packets
	vector<SOCKET>::iterator it = p_vecSocketList.begin();
	unsigned int uiCount = 0;
	//int iNum = 1;
	while(it != p_vecSocketList.end())
	{
		DequeueElement* pstElement = p_stElement;
		while(pstElement != m_pstCurrentAddr->m_pNext)
		{
			uiCount++;
			m_vecObserverList[0]->OnNotify(*it,pstElement->m_szElementAddr,pstElement->m_uiAllocedSize, m_uiCurrentSequence);
			m_uiCurrentSequence++;
			pstElement = pstElement->m_pNext;
		}

		//LOG(INFO) << "Post " << iNum++ << " client(s)"; // For test
		// [NOTE] Set interval to release memory balance, sleep time is just an experience value
		if((uiCount % 20) == 0)
		{
			Sleep(100);
		}

		it++;
	}

	return true;
}

DequeueElement* CircleDequeueBase::GetPreviousAddress(int p_iEntireIndex)
{
	DequeueElement* pElement = m_pstCurrentAddr;

	// If pElement->m_pPrevious equals to m_pstCurrentAddr, which means finding in whole loop
	while(pElement->m_pPrevious != m_pstCurrentAddr)
	{
		if((pElement->m_iEntireIndex == p_iEntireIndex) && (pElement->m_pPrevious->m_iEntireIndex != p_iEntireIndex))
		{
			return pElement->m_pNext;
		}

		pElement = pElement->m_pPrevious;
	}

	LOG(ERROR) << "Cannot find base element in circle queue";
	return NULL;
}

bool CircleDequeueBase::IsSocketExist(SOCKET p_iSocket)
{
	vector<SOCKET>::iterator it = m_vecAlivedSocketList.begin();
	while(it != m_vecAlivedSocketList.end())
	{
		if(*it == p_iSocket)
		{
			LOG(ERROR) << "Socket " << p_iSocket << " is exist in the alive list";
			return true;
		}
		it++;
	}	

	it = m_vecRookieSocketList.begin();
	while(it != m_vecRookieSocketList.end())
	{
		if(*it == p_iSocket)
		{
			LOG(ERROR) << "Socket " << p_iSocket << " is exist in the rookie list";
			return true;
		}
		it++;
	}

	return false;
}

bool CircleDequeueBase::PostData(SOCKET p_iSocket)
{
	EnterCriticalSection(&m_csLock);

	// Verify if last updated date is same with local time
	DateTime clsLocalDate;
	clsLocalDate.SetToCurrentDate();

	if (0 != clsLocalDate - m_clsLastDate)
	{
		LOG(WARNING) << "[POST] Local date(" << clsLocalDate.DateToString() << ") is not same with last DBF date(" << m_clsLastDate.DateToString() << ") . File type : " << (unsigned int)m_ucEntireDataType;
		cout << "[POST] Local date(" << clsLocalDate.DateToString() << ") is not same with last DBF date(" << m_clsLastDate.DateToString() << ") . Cannot post data" << endl;
		m_vecRookieSocketList.push_back(p_iSocket);
		LeaveCriticalSection(&m_csLock);
		return true;
	}
	else
	{
		LOG(WARNING) << "[POST] Local date(" << clsLocalDate.DateToString() << ") is same with last DBF date(" << m_clsLastDate.DateToString() << ") . File type : " << (unsigned int)m_ucEntireDataType;
	}

	if(true == IsSocketExist(p_iSocket))
	{
		LeaveCriticalSection(&m_csLock);
		return false;
	}

	if(0 == m_uiSendBufferLength)
	{
		m_vecRookieSocketList.push_back(p_iSocket);
	}
	else if(0 != m_uiSendBufferLength && 0 == m_pstCurrentAddr->m_uiAllocedSize)
	{
		vector<SOCKET> vecSocket;
		vecSocket.push_back(p_iSocket);
		if(false == Notify(vecSocket,m_pszSendBuffer,m_uiSendBufferLength))
		{
			LeaveCriticalSection(&m_csLock);
			return false;
		}

		m_vecAlivedSocketList.push_back(p_iSocket);
	}
	else
	{
		vector<SOCKET> vecSocket;
		vecSocket.push_back(p_iSocket);
		if(false == Notify(vecSocket,m_pszSendBuffer,m_uiSendBufferLength))
		{
			LeaveCriticalSection(&m_csLock);
			return false;
		}

		// Get elements before current element to client socket
		DequeueElement* pElement = GetPreviousAddress(m_uiSendBufferIndex);
		if(NULL == pElement)
		{
			LeaveCriticalSection(&m_csLock);
			return false;
		}

		// Send elements
		if(0 == pElement->m_uiAllocedSize)
		{// means no increment element hasn't been initialize
			m_vecAlivedSocketList.push_back(p_iSocket);
			LeaveCriticalSection(&m_csLock);
			return true;
		}

		if(false == SendPreviousPackets(vecSocket,pElement))
		{
			LeaveCriticalSection(&m_csLock);
			return false;
		}

		m_vecAlivedSocketList.push_back(p_iSocket);
	}

	LeaveCriticalSection(&m_csLock);
	return true;
}

/************************************************************************/
/* Compressed data format : DataHead + CompressHead + CompressedContent */
/************************************************************************/
bool CircleDequeueBase::CompressData(char* p_szDestBuffer, unsigned int& p_ulDestSize, char* p_szSrcBuffer, unsigned int p_ulSrcSize, unsigned char p_uiDataType)
{
	unsigned int uiLevel = 9;
	unsigned long ulDestLength = p_ulDestSize;
	memset(p_szDestBuffer,0,p_ulDestSize);
	int iReturn = g_funCompress((unsigned char*)(p_szDestBuffer + sizeof(DataHead) + sizeof(CompressHead))
		,(unsigned long*)&ulDestLength
		,(unsigned char*)p_szSrcBuffer
		,p_ulSrcSize
		,uiLevel);

	switch(iReturn)
	{
	case COMPRESSED_OK:
		if (ulDestLength + sizeof(CompressHead) < p_ulDestSize)
		{
			CompressHead* szCompressedHead = (CompressHead*)(p_szDestBuffer + sizeof(DataHead));
			szCompressedHead->m_ulCBLength = p_ulSrcSize;
			szCompressedHead->m_ulCELength = ulDestLength;

			DataHead* szDataHead = (DataHead*)p_szDestBuffer;
			szDataHead->m_ucDataType = p_uiDataType | 0x80;
			szDataHead->m_uiDataLength = ulDestLength + sizeof(CompressHead);

			p_ulDestSize = ulDestLength + sizeof(DataHead) + sizeof(CompressHead);
			return true;
		}
		else
		{
			LOG(ERROR) << "Compressed data length is greater than original length";
			m_iLastError = CD_ERROR_LENGTH_OVERFLOW;
		}
		break;
	case COMPRESSED_MEM_ERROR:
		break;
	case COMPRESSED_BUF_ERROR:
		break;
	default:
		break;
	}

	// Compress fail
	LOG(WARNING) << "Compress fail";
	memcpy(p_szDestBuffer + sizeof(DataHead),p_szSrcBuffer,p_ulSrcSize);
	p_ulDestSize = p_ulSrcSize;
	DataHead* szDataHead = (DataHead*)p_szDestBuffer;
	szDataHead->m_ucDataType = p_uiDataType;
	szDataHead->m_uiDataLength = p_ulSrcSize;

	return true;
}

/************************************************************************/
/* Source format : CompressHead + CompressedContent                     */
/* Destination format : OriginalContent									*/
/************************************************************************/
bool CircleDequeueBase::UncompressData(char* p_szDestBuffer, unsigned int& p_ulDestSize, char* p_szSrcBuffer, unsigned int p_ulSrcSize)
{
	CompressHead* pstCompressedHead = (CompressHead*)p_szSrcBuffer;
	if (NULL == pstCompressedHead)
	{
		return false;
	}

	memset(p_szDestBuffer,0,p_ulDestSize);
	unsigned long ulDestSize = pstCompressedHead->m_ulCBLength * 2 + 12;
	if ((p_szDestBuffer == NULL) || (ulDestSize > p_ulDestSize))
	{
		return false;
	}
	else
	{
		ulDestSize = p_ulDestSize;
	}

	int iReturn = g_funUncompress((Bytef*)p_szDestBuffer,(uLongf *)&ulDestSize,(const Bytef*)(p_szSrcBuffer + sizeof(CompressHead)),(uLong)pstCompressedHead->m_ulCELength);
	switch(iReturn)
	{
	case COMPRESSED_OK:
		if (ulDestSize == pstCompressedHead->m_ulCBLength)
		{
			p_ulDestSize = ulDestSize;
			return true;
		}
	case COMPRESSED_MEM_ERROR:
	case COMPRESSED_BUF_ERROR:
	default:
		break;
	}

	p_ulDestSize = 0;
	return false;
}

int CircleDequeueBase::CompareData(char* p_szSrcBuffer, unsigned int p_uiSrcSize)
{
	// Compare data with entire buffer
	DbfHeadNew* pstOldBufferHead = (DbfHeadNew*)m_pszEntireBuffer;
	FieldElement* pstOldField = (FieldElement*)(m_pszEntireBuffer + sizeof(DbfHeadNew));
	DbfHeadNew* pstNewBufferHead = (DbfHeadNew*)p_szSrcBuffer;
	FieldElement* pstNewField = (FieldElement*)(p_szSrcBuffer + sizeof(DbfHeadNew));
	char* szDestBuffer = m_pszCache;
	unsigned int uiDestBufferSize = m_uiCacheSize;
	memset(szDestBuffer,0,m_uiCacheSize);

	// If head length dismatch then return error code
	if (pstOldBufferHead->m_usHeadLength != pstNewBufferHead->m_usHeadLength)
	{
		m_iLastError = CD_WARNING_LEN_DISMATCH;
		return -1;
	}
	if (0 != memcmp(p_szSrcBuffer,m_pszEntireBuffer,pstNewBufferHead->m_usHeadLength))
	{
		m_iLastError = CD_WARNING_CONTENT_DISMATCH;
		return -1;
	}

	unsigned int uiChangedStockNumber = 0;
	unsigned int uiFieldNumber = (pstNewBufferHead->m_usHeadLength - sizeof(DbfHeadNew))/sizeof(FieldElement);
	unsigned int uiOffset = sizeof(ChangedRecordNumber); // Pre-reserve data head and changed record number in the memory
	for(int i = 0; i < pstNewBufferHead->m_ulRecordNumber; i++)
	{
		unsigned char ucChangedFiledNumber = 0;
		bool bIsFirstChange = true;
		ChangedRecord* szChangedRecord = NULL;
		for (int j = 0; j < uiFieldNumber; j++)
		{
			// Get new field and old field to compare in the following steps
			char* szOldField = (char*)(m_pszEntireBuffer + pstOldBufferHead->m_usHeadLength + i*pstOldBufferHead->m_usRecordLength + pstOldField[j].m_ulOffset);
			char* szNewField = (char*)(p_szSrcBuffer + pstNewBufferHead->m_usHeadLength + i*pstNewBufferHead->m_usRecordLength + pstNewField[j].m_ulOffset);
			if (0 != memcmp(szNewField,szOldField,pstNewField[j].m_cFieldLength))
			{
				if (true == bIsFirstChange)
				{
					szChangedRecord = (ChangedRecord*)(szDestBuffer + uiOffset);
					szChangedRecord->m_usRecordIndex = i;
					uiOffset += sizeof(ChangedRecord);
					bIsFirstChange = false;
				}

				// Construct changed fields struct in the buffer
				ChangedField* szChangedFields = (ChangedField*)(szDestBuffer + uiOffset);
				szChangedFields->m_ucChangedFieldIndex = j;
				uiOffset += sizeof(ChangedField);

				// Copy fields value into the buffer
				memcpy(szDestBuffer+uiOffset,szNewField,pstNewField[j].m_cFieldLength);
				uiOffset += pstNewField[j].m_cFieldLength;
				szChangedRecord->m_ucChangedFieldsNumber++;
			}
		}

		if(false == bIsFirstChange)
		{
			uiChangedStockNumber++;
		}
	}

	// Construct packet and compress data
	if(uiChangedStockNumber > 0)
	{
		ChangedRecordNumber* szRecordNumber = (ChangedRecordNumber*)szDestBuffer;
		szRecordNumber->m_usChangedStockNumber = uiChangedStockNumber;
		uiDestBufferSize = uiOffset;

		// Compress Data
		m_uiComCacheLength = m_uiComCacheSize;
		CompressData(m_szCompressCache,m_uiComCacheLength, szDestBuffer, uiDestBufferSize, m_ucIncrementDataType);
		WriteElement(m_szCompressCache,m_uiComCacheLength);

		return uiChangedStockNumber;
	}

	return 0;
}

bool CircleDequeueBase::CombineData(char* p_szSrcBuffer, unsigned int p_uiSrcSize)
{
	// Variable definition
	unsigned long ulEntireBufferOffset = 0;
	unsigned long ulSourceBufferOffset = 0;
	ChangedRecord* pstChangedRecord = NULL;
	ChangedField* pstChangedField = NULL;

	ChangedRecordNumber* pstChangedRecordsNum = (ChangedRecordNumber*)p_szSrcBuffer;
	DbfHeadNew* pstDBFHead = (DbfHeadNew*)m_pszEntireBuffer;
	FieldElement* pstFieldElement = (FieldElement*)(m_pszEntireBuffer + sizeof(DbfHeadNew));
	ulSourceBufferOffset = sizeof(ChangedRecordNumber);

	// Main combine process
	for (int i = 0; i < pstChangedRecordsNum->m_usChangedStockNumber; i++)
	{
		// Get one changed record
		pstChangedRecord = (ChangedRecord*)(p_szSrcBuffer + ulSourceBufferOffset);
		if (pstChangedRecord->m_usRecordIndex >= pstDBFHead->m_ulRecordNumber)
		{
			//LOG(ERROR) << "Record ID : " << pstChangedRecord->m_usRecordIndex << " >= Record Number : " << pstDBFHead->m_ulRecordNumber;
			return false;
		}

		// Update source buffer offset
		ulSourceBufferOffset += sizeof(ChangedRecord);
		if (ulSourceBufferOffset > p_uiSrcSize)
		{
			//LOG(ERROR) << "Source buffer offset : " << ulSourceBufferOffset << " > Source buffer size : " << p_uiSrcSize;
			return false;
		}

		// Update field
		for (int j = 0; j < pstChangedRecord->m_ucChangedFieldsNumber; j++)
		{
			// Get one changed field
			pstChangedField = (ChangedField*)(p_szSrcBuffer + ulSourceBufferOffset);
			ulSourceBufferOffset += sizeof(ChangedField);
			if ((ulSourceBufferOffset + pstFieldElement[pstChangedField->m_ucChangedFieldIndex].m_cFieldLength) > p_uiSrcSize)
			{
				return false;
			}

			// Update entire buffer offset
			ulEntireBufferOffset = pstDBFHead->m_usHeadLength + pstChangedRecord->m_usRecordIndex*pstDBFHead->m_usRecordLength + pstFieldElement[pstChangedField->m_ucChangedFieldIndex].m_ulOffset;
			if (ulEntireBufferOffset + pstFieldElement[pstChangedField->m_ucChangedFieldIndex].m_cFieldLength > m_uiEntireBufferLength)
			{
				return false;
			}

			// Update changed field to entire buffer
			memcpy(m_pszEntireBuffer + ulEntireBufferOffset, p_szSrcBuffer + ulSourceBufferOffset, pstFieldElement[pstChangedField->m_ucChangedFieldIndex].m_cFieldLength);
			ulSourceBufferOffset += pstFieldElement[pstChangedField->m_ucChangedFieldIndex].m_cFieldLength;
		}
	}

	return true;
}

bool CircleDequeueBase::WriteData(char* p_szBuffer, unsigned int p_uiBufferSize)
{
	return true;
}

bool CircleDequeueBase::WriteData(char* p_szBufferAddress, unsigned int p_uiBufferSize, unsigned char p_cType, bool p_bIsResend)
{
	return true;
}

void CircleDequeueBase::MoveToSendBuffer(unsigned char p_ucFileType)
{
	EnterCriticalSection(&m_csLock);
	// Compress entire data and copy it to send buffer
	m_uiComCacheLength = m_uiComCacheSize;
	CompressData(m_szCompressCache,m_uiComCacheLength,m_pszEntireBuffer,m_uiEntireBufferLength,p_ucFileType);
	memcpy(m_pszSendBuffer,m_szCompressCache,m_uiComCacheLength);
	m_uiSendBufferLength = m_uiComCacheLength;
	m_uiCount = 0;
	m_uiSendBufferIndex++;
	LeaveCriticalSection(&m_csLock);
}

void CircleDequeueBase::WriteElement(char* p_szBufferAddress, unsigned int p_uiBufferSize)
{
	// Find next available element
	DequeueElement* pElement = m_pstCurrentAddr->m_pNext;

	// Compare elements size and create new element if the size needed is larger than current element size
	if(pElement->m_uiSize < p_uiBufferSize)
	{
		// Acquire a memory buffer
		DequeueElement* pstNew = AddElementBefore(pElement,p_uiBufferSize);

		// Copy head and body to buffer
		memcpy(pstNew->m_szElementAddr,p_szBufferAddress,p_uiBufferSize);

		// Assign information
		pstNew->m_uiAllocedSize = p_uiBufferSize;
		pstNew->m_iEntireIndex = m_uiSendBufferIndex;
		m_pstCurrentAddr = pstNew;

		// Re-calculate the amount of elements
		m_uiElementAmount++;
	}
	else
	{
		// Copy head and body to buffer
		memcpy(pElement->m_szElementAddr,p_szBufferAddress,p_uiBufferSize);

		// Assign information
		pElement->m_iEntireIndex = m_uiSendBufferIndex;
		pElement->m_uiAllocedSize = p_uiBufferSize;
		m_pstCurrentAddr = pElement;
	}
}


unsigned int CircleDequeueBase::GetSendBuffer(char** p_szBufferAddress)
{
	if (m_pszSendBuffer == NULL)
	{
		return 0;
	}

	*p_szBufferAddress = m_pszSendBuffer;
	return m_uiSendBufferLength;
}

unsigned int CircleDequeueBase::GetEntireBuffer(char** p_szBufferAddress)
{
	if (m_pszEntireBuffer == NULL)
	{
		return 0;
	}

	*p_szBufferAddress = m_pszEntireBuffer;
	return m_uiEntireBufferLength;
}

void CircleDequeueBase::SetEntireFlag()
{
	EnterCriticalSection(&m_csLock);
	m_uiCount = 0;
	LeaveCriticalSection(&m_csLock);
}